/*
 * Copyright (c) 2008, 2016, OneAPM and/or its affiliates. All rights reserved.
 */
package me.yufan.chess.ui.animations;

/**
 * Title: AnimatedValue
 * Description: Utility class for updating a value using an animation equation.
 *
 * @author Yufan
 * @version 1.0.0
 * @since 2016-01-26 23:13
 */
public class AnimatedValue {
    /**
     * The animation duration, in milliseconds.
     */
    private int duration;

    /**
     * The current time, in milliseconds.
     */
    private int time;

    /**
     * The base value.
     */
    private float base;

    /**
     * The maximum difference from the base value.
     */
    private float diff;

    /**
     * The current value.
     */
    private float value;

    /**
     * The animation equation to use.
     */
    private AnimationEquation eqn;

    /**
     * Constructor.
     *
     * @param duration the total animation duration, in milliseconds
     * @param min      the minimum value
     * @param max      the maximum value
     * @param eqn      the animation equation to use
     */
    public AnimatedValue(int duration, float min, float max, AnimationEquation eqn) {
        this.time = 0;
        this.duration = duration;
        this.value = min;
        this.base = min;
        this.diff = max - min;
        this.eqn = eqn;
    }

    /**
     * Returns the current value.
     */
    public float getValue() {
        return value;
    }

    /**
     * Returns the current animation time, in milliseconds.
     */
    public int getTime() {
        return time;
    }

    /**
     * Sets the animation time manually.
     *
     * @param time the new time, in milliseconds
     */
    public void setTime(int time) {
        this.time = clamp(time, 0, duration);
        updateValue();
    }

    /**
     * Returns the total animation duration, in milliseconds.
     */
    public int getDuration() {
        return duration;
    }

    /**
     * Sets the animation duration.
     *
     * @param duration the new duration, in milliseconds
     */
    public void setDuration(int duration) {
        this.duration = duration;
        int newTime = clamp(time, 0, duration);
        if (time != newTime) {
            this.time = newTime;
            updateValue();
        }
    }

    /**
     * Returns the animation equation being used.
     */
    public AnimationEquation getEquation() {
        return eqn;
    }

    /**
     * Sets the animation equation to use.
     *
     * @param eqn the new equation
     */
    public void setEquation(AnimationEquation eqn) {
        this.eqn = eqn;
        updateValue();
    }

    /**
     * Updates the animation by a delta interval.
     *
     * @param delta the delta interval since the last call.
     * @return true if an update was applied, false if the animation was not updated
     */
    public boolean update(int delta) {
        int newTime = clamp(time + delta, 0, duration);
        if (time != newTime) {
            this.time = newTime;
            updateValue();
            return true;
        }
        return false;
    }

    /**
     * Recalculates the value by applying the animation equation with the current time.
     */
    private void updateValue() {
        float t = eqn.calc((float) time / duration);
        this.value = base + (t * diff);
    }

    private static int clamp(int min, int value, int max) {
        if (value < min)
            return min;
        if (value > max)
            return max;
        return value;
    }
}
